/*
* INSANE - Interactive Structural Analysis Environment
*
* Copyright (C) 2003-2004
* Universidade Federal de Minas Gerais
* Escola de Engenharia
* Departamento de Engenharia de Estruturas
*
* Author's email : insane@dees.ufmg.br
* Author's Website : http://www.dees.ufmg.br/insane
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*/
package br.ufmg.dees.insane.model.xfemModel;

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

import br.ufmg.dees.insane.model.femModel.FemModel;
import br.ufmg.dees.insane.model.xfemModel.crackGrowthDirectionLaw.CrackGrowthDirectionLaw;
import br.ufmg.dees.insane.model.xfemModel.crackGrowthIncrementLaw.CrackGrowthIncrementLaw;
import br.ufmg.dees.insane.model.xfemModel.enrichmentFunction.EnrichmentFunction;
import br.ufmg.dees.insane.model.xfemModel.enrichmentItem.CrackInterior;
import br.ufmg.dees.insane.model.xfemModel.enrichmentItem.CrackTip;
import br.ufmg.dees.insane.model.xfemModel.enrichmentItem.EnrichmentItem;
import br.ufmg.dees.insane.model.xfemModel.geometryDescription.GeometryDescription;
import br.ufmg.dees.insane.model.xfemModel.geometryEntity.GeometryEntity;
import br.ufmg.dees.insane.model.xfemModel.phenomenaDetector.CohesiveCrackPropagation;
import br.ufmg.dees.insane.model.xfemModel.phenomenaDetector.PhenomenaDetector;
import br.ufmg.dees.insane.model.xfemModel.xfemElement.XFEMElement;
import br.ufmg.dees.insane.model.xfemModel.xfemelementNode.XFEMElementNode;
import br.ufmg.dees.insane.util.IPoint3d;

/**
 * @author Kelson Wolff
 *
 */
public class XFemModel extends FemModel {
	
	/**
	 * Comment for <code>serialVersionUID</code>
	 */
	private static final long serialVersionUID = 1L;
	
	private PhenomenaDetector phd;
	
	private List<GeometryEntity> geoEntityList;
	private GeometryDescription geoDescription;
	private List<EnrichmentItem> enrichmentItemList;
	private List<EnrichmentFunction> enrichmentFunctionList;
	private CrackGrowthDirectionLaw directionLaw;
	private CrackGrowthIncrementLaw incrementLaw;
	private List<XFEMElementNode> xfemElmNodesList;
	private PhenomenaDetector crackPropagation;
	private int nStep;
	
    /**
	 * 
	 */
	public XFemModel() {
		super();
		// TODO Auto-generated constructor stub
		geoEntityList = new ArrayList<GeometryEntity>();
		enrichmentItemList = new ArrayList<EnrichmentItem>();
		enrichmentFunctionList = new ArrayList<EnrichmentFunction>();
		xfemElmNodesList = new ArrayList<XFEMElementNode>();
		nStep = 0;
	}

	/**
	 * @return Returns the directionLaw.
	 */
	public CrackGrowthDirectionLaw getDirectionLaw() {
		return directionLaw;
	}

	/**
	 * @param directionLaw The directionLaw to set.
	 */
	public void setDirectionLaw(CrackGrowthDirectionLaw directionLaw) {
		this.directionLaw = directionLaw;
	}

	/**
	 * @return Returns the enrichmentFunctionList.
	 */
	public List<EnrichmentFunction> getEnrichmentFunctionList() {
		return enrichmentFunctionList;
	}

	/**
	 * @param enrichmentFunctionList The enrichmentFunctionList to set.
	 */
	public void setEnrichmentFunctionList(
			List<EnrichmentFunction> enrichmentFunctionList) {
		this.enrichmentFunctionList = enrichmentFunctionList;
	}

	/**
	 * @return Returns the geoDescription.
	 */
	public GeometryDescription getGeoDescription() {
		return geoDescription;
	}
	
	/**
	 * 
	 * @return
	 */
	public String getGeoDescriptionName(){
		return this.getGeoDescription().getClass().getName();
	}

	/**
	 * @param geoDescription The geoDescription to set.
	 */
	public void setGeoDescription(GeometryDescription geoDescription) {
		this.geoDescription = geoDescription;
	}

	/**
	 * @return Returns the geoEntityList.
	 */
	public List<GeometryEntity> getGeoEntityList() {
		return geoEntityList;
	}

	/**
	 * @param geoEntityList The geoEntityList to set.
	 */
	public void setGeoEntityList(List<GeometryEntity> geoEntityList) {
		this.geoEntityList = geoEntityList;
	}

	/**
	 * @return Returns the incrementLaw.
	 */
	public CrackGrowthIncrementLaw getIncrementLaw() {
		return incrementLaw;
	}

	/**
	 * @param incrementLaw The incrementLaw to set.
	 */
	public void setIncrementLaw(CrackGrowthIncrementLaw incrementLaw) {
		this.incrementLaw = incrementLaw;
	}

	/* (non-Javadoc)
     * @see br.ufmg.dees.insane.model.femModel.Model#update()
     */
    public void update() {
    	super.update();
//    	ListIterator elements = this.getElementsList().listIterator();
//        while (elements.hasNext()) {
//            XFEMElement elm = (XFEMElement)elements.next();
//            this.phd.enrichElement(elm);
//        } 
    	this.solveFractureMechanicsProblem();
    }

	/**
	 * @return Returns the phd.
	 */
	public PhenomenaDetector getPhd() {
		return phd;
	}


	/**
	 * @param phd The phd to set.
	 */
	public void setPhd(PhenomenaDetector phd) {
		this.phd = phd;
	}
	
	/**
	 * @return Returns the enrichmentItemList.
	 */
	public List<EnrichmentItem> getEnrichmentItemList() {
		return enrichmentItemList;
	}

	/**
	 * @param enrichmentItemList The enrichmentItemList to set.
	 */
	public void setEnrichmentItemList(List<EnrichmentItem> enrichmentItemList) {
		this.enrichmentItemList = enrichmentItemList;
	}
	
	/**
	 * 
	 * @param enrItem
	 */
	public void setEnrichmentItem(EnrichmentItem enrItem){
		this.enrichmentItemList.add(enrItem);
	}

	/**
	 * 
	 * @param n
	 * @return
	 */
	public EnrichmentItem getEnrichmentItem(int n){
		return this.enrichmentItemList.get(n);
	}
	
	/**
	 * 
	 * @param label
	 * @return
	 */
	public EnrichmentItem getEnrichmentItem(String label){
		ListIterator enrItems = this.getEnrichmentItemList().listIterator();
		while(enrItems.hasNext()){
			EnrichmentItem enrItem = (EnrichmentItem) enrItems.next();
			if(enrItem.getLabel().equals(label))
				return enrItem;
		}
		return null;
	}
	
	/**
	 * 
	 * @param n
	 * @return
	 */
	public EnrichmentFunction getEnrichmentFunction(int n){
		return this.enrichmentFunctionList.get(n);
	}
	
	/**
	 * 
	 * @param n
	 * @return
	 */
	public GeometryEntity getGeoEntity(int n){
		return this.geoEntityList.get(n);
	}
	
	/**
	 * 
	 * @param label
	 * @return
	 */
	public GeometryEntity getGeoEntity(String label){
		ListIterator geoEntities = this.getGeoEntityList().listIterator();
		while(geoEntities.hasNext()){
			GeometryEntity geoEnt = (GeometryEntity) geoEntities.next();
			if(geoEnt.getLabel().equals(label))
				return(geoEnt);
		}
		return null;
	}
	
	/**
	 * 
	 * @param geoEntity
	 */
	public void setGeoEntity(GeometryEntity geoEntity){
		this.geoEntityList.add(geoEntity);
	}
	
	/**
	 * Key method for fracture problem computation. Shoots the methods
	 * for crack localization inside mesh, enrichment of elements splited
	 * by line crack and crack propagation control.
	 */
	public void solveFractureMechanicsProblem(){
		this.nStep++;
		if(this.nStep == 1){
			this.treatMeshGeoInteractionPhase1();
			this.treatMeshGeoInteractionPhase2();
//			this.treatEnrichment();
//			this.resolveConflictsInEnrichment();
//			this.resolveLinearDependencyForEnrichment();
		}
		this.terminate();
	}
	
	/**
	 * This method find elements interacting with crack interiors,
	 * not with crack tips.
	 */
	public void treatMeshGeoInteractionPhase1(){
		ListIterator elms = this.getElementsList().listIterator();
		while(elms.hasNext()){
			XFEMElement e = (XFEMElement) elms.next();
			e.treatGeoMeshInteraction(this.getEnrichmentItemList());
		}
	}
	
	/**
	 * This method find out elements interacted with the discontinuities.
	 */
	public void treatMeshGeoInteractionPhase2(){
		ListIterator enrItems = this.getEnrichmentItemList().listIterator();
		while(enrItems.hasNext()){
			EnrichmentItem enrItem = (EnrichmentItem) enrItems.next();
			if(enrItem instanceof CrackInterior){
				CrackInterior crackInt = (CrackInterior) enrItem;
				crackInt.treatMeshGeoInteractionForMyTips();
			}	
		}
	}
	
	/**
	 * Sets enriched nodes after doing the
	 * mesh-geo interaction
	 */
	public void treatEnrichment(){
		ListIterator enrItems = this.getEnrichmentItemList().listIterator();
		while(enrItems.hasNext()){
			EnrichmentItem enrItem = (EnrichmentItem) enrItems.next();
			enrItem.treatEnrichment();
		}
	}
	
	/**
	 * 
	 *
	 */
	public void resolveConflictsInEnrichment(){
		
	}
	
	/**
	 * 
	 *
	 */
	public void resolveLinearDependencyForEnrichment(){
		
	}
	
	/**
	 * Verifies the conditional test for crack propagation. If the
	 * result is true, crack is allowed to propagate and the enrichment
	 * item accomplish its enrichment update method.
	 */
	public void terminate(){
		if(this.crackPropagation instanceof CohesiveCrackPropagation){
			
			CohesiveCrackPropagation crackPropagation = (CohesiveCrackPropagation)this.getCrackPropagation();
			
			XFEMElement currentTipElm = null;
			ListIterator enrItems = this.getEnrichmentItemList().listIterator();
			while(enrItems.hasNext()){
				EnrichmentItem enrItem = (EnrichmentItem) enrItems.next();
				if(enrItem instanceof CrackTip){
					int lastIndex = ((CrackTip)enrItem).getInteractedElements().size() - 1;
					currentTipElm = ((CrackTip)enrItem).getInteractedElements().get(lastIndex);
					
					boolean propagation = false;
					ListIterator tipElmNeighbors = currentTipElm.getMyNeighbors().listIterator();
					while(tipElmNeighbors.hasNext() && !propagation){
						XFEMElement currentElmNeighbor = (XFEMElement) tipElmNeighbors.next();
						// Conditional test for crack propagation. If test result is true, the neighbor element reached tensile strength.
						if(crackPropagation.verifyPhenomenonOccurrence(currentElmNeighbor) == true){
							// Tests if neighbor element holds the current crack tip on any of its edges...
							double x = ((CrackTip)enrItem).getTip().getCoordinates(0);
							double y = ((CrackTip)enrItem).getTip().getCoordinates(1);
							IPoint3d tip = new IPoint3d(x,y);
							// ...if the crack tip is on any of edges of neighbor element, the crack is allowed to propagate.
							// or if the crack tip is inside current tip element, crack needs propagate to element edge(for step 1).
							if(currentElmNeighbor.isOnEdge(tip) || ((CrackTip)enrItem).getTip().interactsWith(currentTipElm)){
								enrItem.updateEnrichment(this);
								propagation = true;
							}
						}
					}
				}	
			}	 
		}
		else{
			// Compute Stress Intensity Factor... and
			
			// ...Update Enrichment
			ListIterator enrItems = this.getEnrichmentItemList().listIterator();
			while(enrItems.hasNext()){
				EnrichmentItem enrItem = (EnrichmentItem) enrItems.next();
				enrItem.updateEnrichment(this);
			}
		}	
	}

	/**
	 * @return Returns the xfemElmNodesList.
	 */
	public List<XFEMElementNode> getXFEMElmNodesList() {
		return this.xfemElmNodesList;
	}
	
	/**
	 * 
	 * @param label
	 * @return
	 */
	public XFEMElementNode getXFEMElmLabel(String label){
		ListIterator elmNodes = this.getXFEMElmNodesList().listIterator();
		while(elmNodes.hasNext()){
			XFEMElementNode elmNode = (XFEMElementNode) elmNodes.next();
			if(elmNode.getLabel().equals(label)){
				return (elmNode);
			}
		}
		return null;
	}

	/**
	 * @param xfemElmNodesList The xfemElmNodesList to set.
	 */
	public void setXFEMElmNodesList(List<XFEMElementNode> xfemElmNodesList) {
		this.xfemElmNodesList = xfemElmNodesList;
	}
	
	/**
	 * 
	 * @param xfemElmNode
	 */
	public void setXFEMElmNodesList(XFEMElementNode xfemElmNode){
		this.xfemElmNodesList.add(xfemElmNode);
	}

	/**
	 * @return Returns the crackPropagation.
	 */
	public PhenomenaDetector getCrackPropagation() {
		return crackPropagation;
	}

	/**
	 * @param crackPropagation The crackPropagation to set.
	 */
	public void setCrackPropagation(PhenomenaDetector crackPropagation) {
		this.crackPropagation = crackPropagation;
	}

}
